package net.goutalk.glcs.module.app.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.meta.MetaUtil;
import cn.hutool.db.meta.Table;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.github.yulichang.base.MPJBaseServiceImpl;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.FormTemplateType;
import net.goutalk.glcs.common.enums.YesOrNoEnum;
import net.goutalk.glcs.common.exception.MyException;
import net.goutalk.glcs.common.utils.DatasourceUtil;
import net.goutalk.glcs.config.GeneratePathConfig;
import net.goutalk.glcs.module.app.dto.AddAppMenuDto;
import net.goutalk.glcs.module.app.dto.AddFuncDesignDto;
import net.goutalk.glcs.module.app.dto.UpdateAppFuncDesignDto;
import net.goutalk.glcs.module.app.entity.AppFuncDesign;
import net.goutalk.glcs.module.app.entity.AppMenu;
import net.goutalk.glcs.module.app.mapper.AppFuncDesignMapper;
import net.goutalk.glcs.module.app.mapper.AppMenuMapper;
import net.goutalk.glcs.module.app.service.IAppFuncDesignService;
import net.goutalk.glcs.module.generator.dto.GeneratorAppDto;
import net.goutalk.glcs.module.generator.entity.GeneratorConfig;
import net.goutalk.glcs.module.generator.entity.OutputConfig;
import net.goutalk.glcs.module.generator.entity.TableConfig;
import net.goutalk.glcs.module.generator.service.IGeneratorService;
import net.goutalk.glcs.module.generator.utils.GeneratorUtil;
import net.goutalk.glcs.module.generator.utils.SqlUtil;
import net.goutalk.glcs.module.system.service.IDatabaselinkService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author tanyujie
 * @since 2023-07-26
 */
@Service
@AllArgsConstructor
public class AppFuncDesignServiceImpl extends MPJBaseServiceImpl<AppFuncDesignMapper, AppFuncDesign> implements IAppFuncDesignService {


    private final AppMenuMapper appMenuMapper;

    private final IGeneratorService generatorService;

    private final GeneratePathConfig generatePathConfig;

    private final IDatabaselinkService databaselinkService;



    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddFuncDesignDto dto) {

        AppFuncDesign appFuncDesign = BeanUtil.toBean(dto, AppFuncDesign.class);


        //如果是自定义表单  不需要生成代码 以及 page.json
        if (dto.getFormType() == FormTemplateType.CUSTOM.getCode() && dto.getIsGeneratorCode() != YesOrNoEnum.YES.getCode()) {
            AppMenu appMenu = BeanUtil.toBean(dto.getMenuConfigs(), AppMenu.class);


            long snowflakeNextId = IdUtil.getSnowflakeNextId();
            appMenu.setUrl(appMenu.getUrl() + StringPool.QUESTION_MARK + "id=" + snowflakeNextId);

            appMenuMapper.insert(appMenu);

            appFuncDesign.setId(snowflakeNextId);
            appFuncDesign.setAppMenuId(appMenu.getId());
            save(appFuncDesign);

        } else {
            if(dto.getFormType() == FormTemplateType.CUSTOM.getCode()){
                GeneratorConfig generatorConfig = JSONUtil.toBean(dto.getJsonContent(), GeneratorConfig.class);
                generatorEndCode(generatorConfig);
            }
            AppMenu appMenu = BeanUtil.toBean(dto.getMenuConfigs(), AppMenu.class);


            appMenu.setUrl("/pages/" + dto.getCodes().getOutputValue() + StringPool.SLASH + dto.getCodes().getClassName().toLowerCase() + StringPool.SLASH + "list");
            appMenuMapper.insert(appMenu);

            appFuncDesign.setAppMenuId(appMenu.getId());
            save(appFuncDesign);

            //保存成功之后  生成代码
            modifyPageJsonFile(dto.getCodes(), dto.getMenuConfigs());

            generatorService.generateAppCodes(dto.getCodes());
        }
        return Boolean.TRUE;
    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public Boolean modify(UpdateAppFuncDesignDto dto) {

        AppFuncDesign appFuncDesign = BeanUtil.toBean(dto, AppFuncDesign.class);

        //如果是自定义表单  不需要生成代码 以及 page.json
        if (dto.getFormType() == FormTemplateType.CUSTOM.getCode() && dto.getIsGeneratorCode() != YesOrNoEnum.YES.getCode()) {
            AppMenu appMenu = BeanUtil.toBean(dto.getMenuConfigs(), AppMenu.class);

            appMenu.setId(dto.getAppMenuId());
            appMenu.setUrl(appMenu.getUrl() + StringPool.QUESTION_MARK + "id=" + appFuncDesign.getId());

            appMenuMapper.updateById(appMenu);

            appFuncDesign.setAppMenuId(appMenu.getId());
            updateById(appFuncDesign);

        } else {

            if(dto.getFormType() == FormTemplateType.CUSTOM.getCode()){
                GeneratorConfig generatorConfig = JSONUtil.toBean(dto.getJsonContent(), GeneratorConfig.class);
                generatorEndCode(generatorConfig);
            }

            AppMenu appMenu = BeanUtil.toBean(dto.getMenuConfigs(), AppMenu.class);

            appMenu.setId(dto.getAppMenuId());
            appMenu.setUrl("/pages/" + dto.getCodes().getOutputValue() + StringPool.SLASH + dto.getCodes().getClassName().toLowerCase() + StringPool.SLASH + "list");
            appMenuMapper.updateById(appMenu);

            updateById(appFuncDesign);

            //保存成功之后  生成代码
            modifyPageJsonFile(dto.getCodes(), dto.getMenuConfigs());


            generatorService.generateAppCodes(dto.getCodes());
        }


        return Boolean.TRUE;

    }

    private void modifyPageJsonFile(GeneratorAppDto dto, AddAppMenuDto menuDto) {
        File tempFile = FileUtil.file(generatePathConfig.getAppPath() + StringPool.SLASH + "pages.json");

        JSON jsonContent = JSONUtil.readJSON(tempFile, StandardCharsets.UTF_8);

        //如果已经包含了此地址  就不再生成
        if (jsonContent.getByPath("pages").toString().contains( "pages/" + dto.getOutputValue() + StringPool.SLASH + dto.getClassName().toLowerCase() + StringPool.SLASH + "list")) {
            return;
        }

        Map<String, Object> listJsonMap = new HashMap<>();
        listJsonMap.put("path", "pages/" + dto.getOutputValue() + StringPool.SLASH + dto.getClassName().toLowerCase() + StringPool.SLASH + "list");
        Map<String, Object> listStyleMap = new HashMap<>();
        listStyleMap.put("navigationBarTitleText", menuDto.getName());
        listJsonMap.put("style", listStyleMap);


        Map<String, Object> formJsonMap = new HashMap<>();
        formJsonMap.put("path", "pages/" + dto.getOutputValue() + StringPool.SLASH + dto.getClassName().toLowerCase() + StringPool.SLASH + "form");
        Map<String, Object> formStyleMap = new HashMap<>();
        formStyleMap.put("navigationBarTitleText", "新增");
        formJsonMap.put("style", formStyleMap);

        JSONArray pages = JSONUtil.parseArray(jsonContent.getByPath("pages").toString());

        pages.add(listJsonMap);
        pages.add(formJsonMap);

        jsonContent.putByPath("pages", pages);

        FileUtil.writeString(jsonContent.toJSONString(4), tempFile, StandardCharsets.UTF_8);
    }


    /**
     * 根据json 生成后端代码
     * @param generatorConfig
     */
    @SneakyThrows
    private void generatorEndCode(GeneratorConfig generatorConfig){

        Optional<TableConfig> tableConfigOptional = generatorConfig.getTableConfigs().stream().filter(TableConfig::getIsMain).findFirst();
        //主表
        TableConfig mainTable;
        if (tableConfigOptional.isPresent()) {
            mainTable = tableConfigOptional.get();
        } else {
            throw new MyException("请选择主表");
        }

        OutputConfig outputConfig = generatorConfig.getOutputConfig();
        String databaseId = generatorConfig.getDatabaseId();
        if (BooleanUtils.isTrue(outputConfig.getIsDataAuth())) {
            // 添加权限字段 rule_user_id
            DataSource dataSource = DatasourceUtil.getDataSource(databaseId);
            String[] columnNames = MetaUtil.getColumnNames(dataSource, mainTable.getTableName());
            if (!ArrayUtils.contains(columnNames, GlobalConstant.AUTH_USER_ID)) {
                DbType dbType = databaselinkService.getDbType(databaseId);
                Db.use(dataSource).executeBatch(SqlUtil.buildAddDataAuthFieldSqls(dbType, mainTable.getTableName()));
            }
        }

        //修改 getTableInfos  也记得修改 FromTemplateServiceImpl 的同名方法
        List<Table> tableInfos = getTableInfos(generatorConfig);

        createCodeFile(generatorConfig, mainTable, tableInfos);
    }

    /**
     * 获取表结构信息
     *
     * @param generatorConfig
     * @return
     */
    private List<Table> getTableInfos(GeneratorConfig generatorConfig) {
        List<Table> tableInfos = new ArrayList<>();
        for (TableConfig tableConfig : generatorConfig.getTableConfigs()) {
            //判断是否为默认数据源
            if (StrUtil.equalsIgnoreCase(generatorConfig.getDatabaseId(), GlobalConstant.DEFAULT_DATASOURCE_KEY)) {
                tableInfos.add(MetaUtil.getTableMeta(DatasourceUtil.getDatasourceMaster(), tableConfig.getTableName()));
            } else {
                tableInfos.add(MetaUtil.getTableMeta(DatasourceUtil.getDataSource(generatorConfig.getDatabaseId()), tableConfig.getTableName()));
            }
        }
        return tableInfos;
    }

    /**
     * 根据配置生成文件
     *
     * @param generatorConfig
     * @param mainTable
     * @param tableInfos
     * @throws FileNotFoundException
     */
    private void createCodeFile(GeneratorConfig generatorConfig, TableConfig mainTable, List<Table> tableInfos) throws FileNotFoundException {
        String className = generatorConfig.getOutputConfig().getClassName();
        String dirValue = generatorConfig.getOutputConfig().getOutputValue();

        //---------------------------------------生成entity开始----------------------------------------------------
        {
            Map<String, String> entityCodeMap = GeneratorUtil.getEntityCode(generatorConfig, tableInfos);

            for (Map.Entry<String, String> entry : entityCodeMap.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getEntityOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //---------------------------------------生成entity结束----------------------------------------------------


        //********************************************生成dto开始************************************************
        {
            Map<String, String> dtoMap = new HashMap<>(3);
            dtoMap.putAll(GeneratorUtil.getAddDtoCode(generatorConfig, tableInfos));
            dtoMap.putAll(GeneratorUtil.getUpdateDtoCode(generatorConfig, tableInfos));
            dtoMap.putAll(GeneratorUtil.getPageDtoCode(generatorConfig, tableInfos, mainTable));

            for (Map.Entry<String, String> entry : dtoMap.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getDtoOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //********************************************生成dto结束************************************************

        //###############################################生成Vo开始###############################################
        {
            Map<String, String> voMap = new HashMap<>(2);
            voMap.putAll(GeneratorUtil.getPageVoCode(generatorConfig, tableInfos, mainTable));
            voMap.putAll(GeneratorUtil.getInfoVoCode(generatorConfig, tableInfos, mainTable));

            for (Map.Entry<String, String> entry : voMap.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getVoOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //###############################################生成Vo结束###############################################


        //---------------------------------------生成三层代码开始----------------------------------------------------
        {
            Map<String, String> mapperCode = GeneratorUtil.getMapperCode(generatorConfig, tableInfos);

            for (Map.Entry<String, String> entry : mapperCode.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getMapperOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> serviceCode = GeneratorUtil.getServiceCode(generatorConfig, mainTable, tableInfos.size() > 1);

            for (Map.Entry<String, String> entry : serviceCode.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getServiceOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> serviceImplCode = GeneratorUtil.getServiceImplCode(generatorConfig, mainTable, tableInfos.size() > 1);
            for (Map.Entry<String, String> entry : serviceImplCode.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getServiceImplOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }

        {
            Map<String, String> controllerCode = GeneratorUtil.getControllerCode(generatorConfig, tableInfos, mainTable);
            for (Map.Entry<String, String> entry : controllerCode.entrySet()) {
                GeneratorUtil.writeFile(GeneratorUtil.getControllerOutputDir(dirValue), entry.getKey() + StringPool.DOT_JAVA, entry.getValue());
            }
        }
        //---------------------------------------生成三层代码结束----------------------------------------------------




    }
}
